#include "wx/wxprec.h"

#if wxUSE_BUTTON

#include "wx/button.h"
#include "wx/stockitem.h"
#include "wx/gtk/private.h"
#include "wx/gtk/win_gtk.h"

class wxButton;

extern bool   g_blockEventsOnDrag;

extern "C" {
  static void gtk_button_clicked_callback( GtkWidget *( widget ), wxButton *button ) {
    if( g_isIdle ) {
      wxapp_install_idle_handler();
    }
    if( !button->m_hasVMT ) {
      return;
    }
    if( g_blockEventsOnDrag ) {
      return;
    }
    wxCommandEvent event( wxEVT_COMMAND_BUTTON_CLICKED, button->GetId() );
    event.SetEventObject( button );
    button->GetEventHandler()->ProcessEvent( event );
  }
}

static gint
gtk_button_style_set_callback( GtkWidget *m_widget, GtkStyle *( style ), wxButton *win ) {
  if( g_isIdle ) {
    wxapp_install_idle_handler();
  }
  int left_border = 0;
  int right_border = 0;
  int top_border = 0;
  int bottom_border = 0;
  /* the default button has a border around it */
  if( GTK_WIDGET_CAN_DEFAULT( m_widget ) ) {
    GtkBorder *default_border = NULL;
    gtk_widget_style_get( m_widget, "default_border", &default_border, NULL );
    if( default_border ) {
      left_border += default_border->left;
      right_border += default_border->right;
      top_border += default_border->top;
      bottom_border += default_border->bottom;
      gtk_border_free( default_border );
    }
    win->MoveWindow(
      win->m_x - left_border,
      win->m_y - top_border,
      win->m_width + left_border + right_border,
      win->m_height + top_border + bottom_border );
  }
  return FALSE;
}

//-----------------------------------------------------------------------------
// wxButton
//-----------------------------------------------------------------------------

IMPLEMENT_DYNAMIC_CLASS( wxButton, wxControl )

wxButton::wxButton() {
}

wxButton::~wxButton() {
}

bool wxButton::Create( wxWindow *parent, wxWindowID id, const wxString &label,
                       const wxPoint &pos, const wxSize &size,
                       long style, const wxValidator& validator, const wxString &name ) {
  m_needParent = true;
  m_acceptsFocus = true;
  if( !PreCreation( parent, pos, size ) ||
      !CreateBase( parent, id, pos, size, style, validator, name ) ) {
    wxFAIL_MSG( wxT( "wxButton creation failed" ) );
    return false;
  }
  m_widget = gtk_button_new_with_mnemonic( "" );
  float x_alignment = 0.5;
  if( HasFlag( wxBU_LEFT ) ) {
    x_alignment = 0.0;
  } else if( HasFlag( wxBU_RIGHT ) ) {
    x_alignment = 1.0;
  }
  float y_alignment = 0.5;
  if( HasFlag( wxBU_TOP ) ) {
    y_alignment = 0.0;
  } else if( HasFlag( wxBU_BOTTOM ) ) {
    y_alignment = 1.0;
  }
  if( !gtk_check_version( 2, 4, 0 ) ) {
    gtk_button_set_alignment( GTK_BUTTON( m_widget ), x_alignment, y_alignment );
  } else  {
    if( GTK_IS_MISC( GTK_BIN( m_widget )->child ) )
      gtk_misc_set_alignment( GTK_MISC( GTK_BIN( m_widget )->child ),
                              x_alignment, y_alignment );
  }
  SetLabel( label );
  if( style & wxNO_BORDER ) {
    gtk_button_set_relief( GTK_BUTTON( m_widget ), GTK_RELIEF_NONE );
  }
  g_signal_connect_after( m_widget, "clicked",
                          G_CALLBACK( gtk_button_clicked_callback ),
                          this );
  g_signal_connect_after( m_widget, "style_set",
                          G_CALLBACK( gtk_button_style_set_callback ),
                          this );
  m_parent->DoAddChild( this );
  PostCreation( size );
  return true;
}


void wxButton::SetDefault() {
  wxTopLevelWindow *tlw = wxDynamicCast( wxGetTopLevelParent( this ), wxTopLevelWindow );
  wxCHECK_RET( tlw, _T( "button without top level window?" ) );
  tlw->SetDefaultItem( this );
  GTK_WIDGET_SET_FLAGS( m_widget, GTK_CAN_DEFAULT );
  gtk_widget_grab_default( m_widget );
  // resize for default border
  gtk_button_style_set_callback( m_widget, NULL, this );
}

/* static */
wxSize wxButtonBase::GetDefaultSize() {
  static wxSize size = wxDefaultSize;
  if( size == wxDefaultSize ) {
    // NB: Default size of buttons should be same as size of stock
    //     buttons as used in most GTK+ apps. Unfortunately it's a little
    //     tricky to obtain this size: stock button's size may be smaller
    //     than size of button in GtkButtonBox and vice versa,
    //     GtkButtonBox's minimal button size may be smaller than stock
    //     button's size. We have to retrieve both values and combine them.
    GtkWidget *wnd = gtk_window_new( GTK_WINDOW_TOPLEVEL );
    GtkWidget *box = gtk_hbutton_box_new();
    GtkWidget *btn = gtk_button_new_from_stock( GTK_STOCK_CANCEL );
    gtk_container_add( GTK_CONTAINER( box ), btn );
    gtk_container_add( GTK_CONTAINER( wnd ), box );
    GtkRequisition req;
    gtk_widget_size_request( btn, &req );
    gint minwidth, minheight;
    gtk_widget_style_get( box,
                          "child-min-width", &minwidth,
                          "child-min-height", &minheight,
                          NULL );
    size.x = wxMax( minwidth, req.width );
    size.y = wxMax( minheight, req.height );
    gtk_widget_destroy( wnd );
  }
  return size;
}

void wxButton::SetLabel( const wxString &lbl ) {
  wxCHECK_RET( m_widget != NULL, wxT( "invalid button" ) );
  wxString label( lbl );
  if( label.empty() && wxIsStockID( m_windowId ) ) {
    label = wxGetStockLabel( m_windowId );
  }
  wxControl::SetLabel( label );
  const wxString labelGTK = GTKConvertMnemonics( label );
  if( wxIsStockID( m_windowId ) && wxIsStockLabel( m_windowId, label ) ) {
    const char *stock = wxGetStockGtkID( m_windowId );
    if( stock ) {
      gtk_button_set_label( GTK_BUTTON( m_widget ), stock );
      gtk_button_set_use_stock( GTK_BUTTON( m_widget ), TRUE );
      return;
    }
  }
  gtk_button_set_label( GTK_BUTTON( m_widget ), wxGTK_CONV( labelGTK ) );
  gtk_button_set_use_stock( GTK_BUTTON( m_widget ), FALSE );
  ApplyWidgetStyle( false );
}

bool wxButton::Enable( bool enable ) {
  bool isEnabled = IsEnabled();
  if( !wxControl::Enable( enable ) ) {
    return false;
  }
  gtk_widget_set_sensitive( GTK_BIN( m_widget )->child, enable );
  if( !isEnabled && enable ) {
    wxGtkFixSensitivity( this );
  }
  return true;
}

GdkWindow *wxButton::GTKGetWindow( wxArrayGdkWindows& ( windows ) ) const {
  return GTK_BUTTON( m_widget )->event_window;
}

void wxButton::DoApplyWidgetStyle( GtkRcStyle *style ) {
  gtk_widget_modify_style( m_widget, style );
  GtkWidget *child = GTK_BIN( m_widget )->child;
  gtk_widget_modify_style( child, style );
  // in gtk+ 2.12, in case of button with image, the path to the label is:
  //  GtkButton -> GtkAlignment -> GtkHBox -> GtkLabel
  if( GTK_IS_ALIGNMENT( child ) ) {
    GtkWidget *box = GTK_BIN( child )->child;
    if( GTK_IS_BOX( box ) ) {
      for( GList* item = GTK_BOX( box )->children; item; item = item->next ) {
        GtkBoxChild* boxChild = wx_static_cast( GtkBoxChild*, item->data );
        gtk_widget_modify_style( boxChild->widget, style );
      }
    }
  }
}

wxSize wxButton::DoGetBestSize() const {
  // the default button in wxGTK is bigger than the other ones because of an
  // extra border around it, but we don't want to take it into account in
  // our size calculations (otherwsie the result is visually ugly), so
  // always return the size of non default button from here
  const bool isDefault = GTK_WIDGET_HAS_DEFAULT( m_widget );
  if( isDefault ) {
    // temporarily unset default flag
    GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_DEFAULT );
  }
  wxSize ret( wxControl::DoGetBestSize() );
  if( isDefault ) {
    // set it back again
    GTK_WIDGET_SET_FLAGS( m_widget, GTK_CAN_DEFAULT );
  }
  if( !HasFlag( wxBU_EXACTFIT ) ) {
    wxSize defaultSize = GetDefaultSize();
    if( ret.x < defaultSize.x ) {
      ret.x = defaultSize.x;
    }
    if( ret.y < defaultSize.y ) {
      ret.y = defaultSize.y;
    }
  }
  CacheBestSize( ret );
  return ret;
}

// static
wxVisualAttributes
wxButton::GetClassDefaultAttributes( wxWindowVariant ( variant ) ) {
  return GetDefaultAttributesFromGTKWidget( gtk_button_new );
}

#endif // wxUSE_BUTTON
